package de.hsowl.tod4a.client;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.List;

import de.hsowl.tod4a.client.query.Comparator;
import de.hsowl.tod4a.global.exception.NotImplementedException;
import de.hsowl.tod4a.global.exception.ObjectDatabaseException;
import de.hsowl.tod4a.global.exception.WrongObjectException;
import de.hsowl.tod4a.global.listener.DataChangedListener;
import de.hsowl.tod4a.global.model.ClientProperty;
import de.hsowl.tod4a.global.model.ServerProperty;

/**
 * Interface between User and Database. Pass through Objects and Querys and
 * checks them.
 * 
 * @author Sebastian Witt
 * 
 */
public class EntityManagerImpl implements EntityManager {

	private ConnectionInterface conninterface = null;

	public EntityManagerImpl(File pathToDB) throws ObjectDatabaseException {
		conninterface = new EmbeddedConnectionInterface(pathToDB);
	}

	public EntityManagerImpl(File pathToDB, ServerProperty prop) throws ObjectDatabaseException {
		conninterface = new EmbeddedConnectionInterface(pathToDB, prop);
	}

	public EntityManagerImpl(String url, int port, String username, String password, ClientProperty props) throws IOException,
			ObjectDatabaseException {
		conninterface = new RemoteConnectionInterface(url, port, username, password, props);
	}

	/**
	 * See {@link EntityManager#store(Object...)}
	 */
	public void store(Object... objects) throws ObjectDatabaseException {
		store(objects, (String[]) null);
	}

	/**
	 * See {@link EntityManager#store(Object, String)}
	 */
	public void store(Object object, String name) throws ObjectDatabaseException {
		store(new Object[] { object }, new String[] { name });

	}

	/**
	 * See {@link EntityManager#store(Object[], String[])}
	 */
	public void store(Object[] objects, String[] names) throws WrongObjectException {
		if (objects != null && objects.length > 0) {

			for (Object o : objects) {
				if (o.getClass().getName().equals(Object.class.getName())) {
					throw new WrongObjectException(Object.class.getName() + " can not be stored.");
				}
				boolean default_ = false;
				for (Constructor<?> con : o.getClass().getDeclaredConstructors()) {
					con.setAccessible(true);
					if (con.getParameterTypes().length == 0) {
						default_ = true;
						break;
					}
				}
				if (default_ == false) {
					throw new WrongObjectException(o.getClass().getName()
							+ " can not be stored. No private or public default constructor found!");
				}
			}

			conninterface.store(names, objects);
		} else {
			throw new NullPointerException("No object to store was found!");
		}
	}

	/**
	 * See {@link EntityManager#remove(Object...)}
	 */
	public void remove(Object... objects) throws ObjectDatabaseException {
		remove(true, objects);
	}

	/**
	 * See {@link EntityManager#getByClass(Class)}
	 */
	public <T> List<T> getByClass(Class<?> clazz) {
		return getByClass(clazz, Integer.MAX_VALUE);
	}

	/**
	 * See {@link EntityManager#begin()}
	 */
	public void begin() {
		conninterface.begin();
	}

	/**
	 * See {@link EntityManager#commit()}
	 * 
	 * @return
	 * @throws Exception
	 */
	public boolean commit() throws ObjectDatabaseException {
		return conninterface.commitTransactions();
	}

	/**
	 * See {@link EntityManager#remove(boolean, Object...)}
	 */
	public void remove(boolean children, Object... objects) throws ObjectDatabaseException {
		if (objects != null && objects.length > 0) {
			conninterface.remove(children, objects);
		} else {
			throw new NullPointerException("No object to remove was found!");
		}
	}

	/**
	 * See {@link EntityManager#addUser(String, String)}
	 */
	public void addUser(String username, String password) throws ObjectDatabaseException {
		if (password == null || username == null || username.equals("") || password.equals("")) {
			throw new NullPointerException("Wrong values!");
		} else {
			conninterface.addUser(username, password);
		}

	}

	/**
	 * See {@link EntityManager#removeUser(String)}
	 */
	public void removeUser(String username) throws ObjectDatabaseException {
		if (username == null || username.equals("")) {
			throw new NullPointerException("Wrong values!");
		} else {
			conninterface.removeUser(username);
		}
	}

	/**
	 * See {@link EntityManager#close()}
	 */
	public void close() {
		conninterface.close();
	}

	/**
	 * See {@link EntityManager#get(Comparator)}
	 */
	public <T> List<T> get(Comparator<T> comperator) {
		return get(comperator, Integer.MAX_VALUE);
	}

	/**
	 * See {@link EntityManager#getByName(String, Class)}
	 */
	public <T> List<T> getByName(String name, Class<?> clazz) {
		return getByName(name, clazz, Integer.MAX_VALUE);
	}

	/**
	 * See {@link EntityManager#getByName(String)}
	 */
	public List<Object> getByName(String name) {
		return getByName(name, Integer.MAX_VALUE);
	}

	/**
	 * See {@link EntityManager#getByOQL(String, Class)}
	 */
	public <T> List<T> getByOQL(String query, Class<?> clazz) {
		throw new NotImplementedException();
	}

	/**
	 * See {@link EntityManager#getByOQL(String)}
	 */
	public List<Object> getByOQL(String query) {
		throw new NotImplementedException();
	}

	/**
	 * See {@link EntityManager#rollback()}
	 * 
	 * @throws ObjectDatabaseException
	 */
	public void rollback() throws ObjectDatabaseException {
		rollback(1);
	}

	/**
	 * See {@link EntityManager#rollback(int)}
	 * 
	 * @throws ObjectDatabaseException
	 */
	public void rollback(int num) throws ObjectDatabaseException {
		if (num <= 5 && num > 0) {
			conninterface.rollback(num);
		} else {
			throw new IllegalArgumentException("Only the last 1 to 5 transactions can be rolled back");
		}
	}

	/**
	 * See {@link EntityManager#addDataChangedListener(DataChangedListener)}
	 */
	public void addDataChangedListener(DataChangedListener listener) {
		conninterface.addDataChangedListener(listener);
	}

	/**
	 * See {@link EntityManager#get(Comparator, int)}
	 */
	public <T> List<T> get(Comparator<T> comperator, int depth) {
		if (depth < 0) {
			throw new IllegalArgumentException("depth has to be > 0");
		} else if (comperator != null) {
			return conninterface.get(comperator, depth);
		} else {
			throw new NullPointerException("Comperator == null");
		}
	}

	/**
	 * See {@link EntityManager#getByClass(Class, int)}
	 */
	public <T> List<T> getByClass(Class<?> clazz, int depth) {
		if (depth < 0) {
			throw new IllegalArgumentException("depth has to be > 0");
		} else if (clazz != null) {
			return conninterface.getByClass(clazz, depth);
		} else {
			throw new NullPointerException("Class is null");
		}
	}

	/**
	 * See {@link EntityManager#getByName(String, int)}
	 */
	public List<Object> getByName(String name, int depth) {
		if (depth < 0) {
			throw new IllegalArgumentException("depth has to be > 0");
		} else if (name != null && name.length() > 0) {
			return conninterface.getByName(name, depth);
		} else {
			throw new NullPointerException("name == null");
		}
	}

	/**
	 * See {@link EntityManager#getByName(String, Class, int)}
	 */
	public <T> List<T> getByName(String name, Class<?> clazz, int depth) {
		if (depth < 0) {
			throw new IllegalArgumentException("depth has to be > 0");
		} else if (name != null && name.length() > 0) {
			return conninterface.getByName(name, clazz, depth);
		} else {
			throw new NullPointerException("name == null");
		}
	}
}
